package project.ia04.agents.retail;

import jade.core.Agent;
import jade.core.behaviours.CyclicBehaviour;
import jade.core.behaviours.OneShotBehaviour;
import jade.domain.DFService;
import jade.domain.FIPAException;
import jade.domain.FIPAAgentManagement.DFAgentDescription;
import jade.domain.FIPAAgentManagement.ServiceDescription;
import jade.lang.acl.ACLMessage;
import jade.lang.acl.MessageTemplate;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Map;
import java.util.Random;

import org.codehaus.jackson.JsonParseException;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.ObjectMapper;

import project.ia04.utils.Data;

import com.hp.hpl.jena.query.Query;
import com.hp.hpl.jena.query.QueryExecution;
import com.hp.hpl.jena.query.QueryExecutionFactory;
import com.hp.hpl.jena.query.QueryFactory;
import com.hp.hpl.jena.query.QuerySolution;
import com.hp.hpl.jena.query.ResultSet;
import com.hp.hpl.jena.query.Syntax;
import com.hp.hpl.jena.rdf.model.Model;
import com.hp.hpl.jena.rdf.model.ModelFactory;

@SuppressWarnings("serial")
public class RetailAgent extends Agent {

	private static String PREFIX = "PREFIX foaf: <http://xmlns.com/foaf/0.1/>\nPREFIX project: <http://project#>\nPREFIX xsd:<http://www.w3.org/2001/XMLSchema#>\nPREFIX ns: <http://example.org/ns#>\n";

	public void deserializeItems(final String content) {
		final ObjectMapper mapper = new ObjectMapper();
		Map<String, Object> map;
		try {
			map = mapper.readValue(content, Map.class);
			this.gameGenre = (String) map.get(Data.GAMEGENRE);
			this.gameType = (String) map.get(Data.GAMETYPE);
			this.sampleSize = (Integer) map.get(Data.SAMPLESIZE);
			this.fame = (Integer) map.get(Data.FAME);
		} catch (final JsonParseException e) {
			e.printStackTrace();
		} catch (final JsonMappingException e) {
			e.printStackTrace();
		} catch (final IOException e) {
			e.printStackTrace();
		}

		System.out.println("SampleSize : " + this.sampleSize + " / fame : "
				+ this.fame);
	}

	public class RetailListenerBehaviour extends CyclicBehaviour {

		@Override
		public void action() {
			final MessageTemplate template = MessageTemplate
					.MatchPerformative(ACLMessage.REQUEST);
			final ACLMessage message = this.myAgent.receive(template);
			if (message != null) {
				deserializeItems(message.getContent());
				final Random random = new Random();
				RetailAgent.this.startSample = random.nextInt(Data.NGAMER
						- RetailAgent.this.sampleSize);
				RetailAgent.this.endSample = RetailAgent.this.startSample
						+ RetailAgent.this.sampleSize;
				System.out.println("SampleSize : "
						+ RetailAgent.this.startSample + " / "
						+ RetailAgent.this.endSample);
				this.myAgent.addBehaviour(new SellGameBehaviour(message));
			} else {
				block();
			}
		}

	}

	private class SellGameBehaviour extends OneShotBehaviour {

		private final ACLMessage message;

		public SellGameBehaviour(final ACLMessage m) {
			this.message = m;
		}

		@Override
		public void action() {
			final ACLMessage reply = this.message.createReply();
			reply.setPerformative(ACLMessage.INFORM);
			reply.setContent(runExecQuery(queryText()));
			this.myAgent.send(reply);
		}
	}

	private String gameGenre = "RPG";

	private String gameType = "Dungeon";

	private int sampleSize = 3;

	private int fame = 1;

	private int startSample;

	private int endSample;

	private String queryText() {
		String queryText = PREFIX + "\nSELECT Count(DISTINCT ?p) WHERE { \n";
		final ArrayList<String> listGenre = Data.nearestNeightbor(
				this.gameGenre, this.fame);
		final ArrayList<String> listType = Data.nearestNeightbor(this.gameType,
				this.fame + 1);

		for (int i = 0; i < listGenre.size(); i++) {
			for (int j = 0; j < listType.size(); j++) {
				queryText += "    { ?p foaf:topic_interest project:"
						+ listGenre.get(i)
						+ " . ?p foaf:topic_interest project:"
						+ listType.get(j)
						+ ". ?p ns:p ?c. FILTER((?c >= "
						+ this.startSample
						+ ") && (?c <= "
						+ this.endSample
						+ "))}\n    UNION\n    { ?l foaf:knows ?p. ?p foaf:topic_interest project:"
						+ listGenre.get(i)
						+ ". ?p foaf:topic_interest project:" + listType.get(j)
						+ ". ?l ns:p ?c. FILTER((?c >= " + this.startSample
						+ ") && (?c <= " + this.endSample + "))}\n";
				if ((i == (listGenre.size() - 1))
						&& (j == (listType.size() - 1))) {
					queryText += "}";
				} else {
					queryText += "    UNION\n";
				}
			}
		}
		return queryText;
	}

	private Model model;

	private void readModel() {
		this.model = null;
		try {
			this.model = ModelFactory.createDefaultModel();
			this.model.read("file:./KnowledgeBase.foaf", null, "TURTLE");
		} catch (final Exception ex) {
			ex.printStackTrace();
		}
	}

	private String runExecQuery(final String qString) {
		final Query query = QueryFactory.create(qString, Syntax.syntaxARQ);
		final ByteArrayOutputStream output = new ByteArrayOutputStream();
		final QueryExecution queryExecution = QueryExecutionFactory.create(
				query, this.model);
		final ResultSet r = queryExecution.execSelect();
		final QuerySolution sol = r.next();
		String res = sol.get(".1").toString();
		res = res.substring(0, res.indexOf("^"));
		System.out.println(res);
		queryExecution.close();
		return res;
	}

	/*
	 * private String runExecQuery(final String qString) { final Query query =
	 * QueryFactory.create(qString, Syntax.syntaxARQ); final
	 * ByteArrayOutputStream output = new ByteArrayOutputStream(); final
	 * QueryExecution queryExecution = QueryExecutionFactory.create( query,
	 * this.model); final ResultSet r = queryExecution.execSelect();
	 * 
	 * ResultSetFormatter.outputAsJSON(output, r);
	 * 
	 * final QuerySolution sol = r.next(); System.err.println(sol);
	 * 
	 * System.out.println(output); queryExecution.close(); return
	 * output.toString(); }
	 */

	@Override
	public void setup() {
		System.out.println(this.getLocalName() + " --> created!");
		readModel();
		register();
		System.out.println(this.getLocalName() + " --> end setup()!");
		this.addBehaviour(new RetailListenerBehaviour());
	}

	private void register() {
		final DFAgentDescription dfd = new DFAgentDescription();
		final ServiceDescription sd = new ServiceDescription();
		sd.setType("IA04Project");
		sd.setName("RetailAgent");
		dfd.addServices(sd);
		try {
			DFService.register(this, dfd);
		} catch (final FIPAException e) {
			e.printStackTrace();
		}
	}

}
